#include <stdint.h>
#include <stdfunc.h>
#include <memory.h>
#include <text_video.h>
#include <task.h>

// EXPERIMENT: interrupt-less fully cooperative multi-tasking
// tasks give up their time slices at will (pre-determined communist tasking!)

// pros:  
//  - maximum CPU utilization if done correctly and we don't have a lazy task incident (also applies to communism in general)
//  - finely granulated time slices
//  - no overhead from interrupts and ISR code

// cons: 
//  - a little more code on the developers side
//  - a closer eye must be kept to ensure fair time slicing


// G.U.T.S. (Give Up Time Slice)  ;-)
void sched(void) 
{  
    /* find the next task (in order) */
    while(1)
    {
        active_task_id++;
        active_task_id %= MAX_TASKS;
        
        if(task[active_task_id].id != 0)
        {break;}
    }     

    active_task_esp = (uint32_t)task[active_task_id].stack;
}

void add_task(const uint32_t entry, const char * name)
{
    current_id++;
    active_task_count++;
    
    active_task_id = current_id;
    
    /* give us some identity! */
    task[active_task_id].id = active_task_id;
    
    /* copy a null-terminated name string */
    copy_memory((uint8_t*)task[active_task_id].name, (uint8_t*)name, min(strlen(name)+1, 31));
    task[active_task_id].name[31] = 0x00;
    
    /* memalloc up a new stack space */
    task[active_task_id].stack = (uint32_t*)(memalloc(2048, (uint8_t)(4)) + 2048);
    *--task[active_task_id].stack = (uint32_t)(entry);
    
    //printf("TSK: adding task #%u (\"%s\") (entry = %x, stack = %x)\n", active_task_id, task[active_task_id].name, 
    //                                                                   entry, (uint32_t)task[active_task_id].stack);
    
    active_task_esp = (uint32_t)task[active_task_id].stack;
    
    /* setup the tasks' stack and jump to the entry point */
    asm volatile("mov %0, %%esp; jmp *%%eax;"::"r"(task[active_task_id].stack), "a"(entry));
    
    for(;;);
}

void kill_task(const uint32_t id)
{
    /* just set the ID value to -1 and our scheduler will ignore this entry */
    //printf("TSK: killing task #%u (\"%s\")\n", id, task[id].name);
    task[id].id = 0;
    active_task_count--;
    GUTS();
}

void setup_multitasking(void)
{
    uint32_t i;
    
    /* null all the task entries */
    for(i = 0; i < MAX_TASKS; i++)
    {task[i].id = 0;}
    
    active_task_id = current_id = 0;
    active_task_count = 0;
    
    /* start our main kernel task (all other tasks will be spawned from this task) */
    add_task((uint32_t)kmain_task, "kernel task");
}







